#include "collection.h"
#include <QMessageBox> // Warning dialog
#include <QApplication>

QSqlDatabase Collection::m_db;
QMutex Collection::m_mutex;

bool Collection::ensureConnection()
{    
    QMutexLocker locker(&m_mutex);

    if (m_db.isValid()) return true;

    QSettings settings;

    m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setHostName("localhost");

    if (!settings.value("collection/path").isValid()) {
        qWarning() << "Not configured properly";
        return false;
    }

    m_db.setDatabaseName(settings.value("collection/path").toString() + "/videos.db");
    qWarning() << m_db.databaseName();

    if (!m_db.open()) {
        qWarning() << m_db.lastError().text();

        QMessageBox::critical(0, "Cannot open database",
                    "Unable to establish a database connection.\n"
                    "This application needs SQLite support.\n"
                    "Click Cancel to exit.", QMessageBox::Cancel);
        QApplication::quit();
    }

    if (m_db.tables(QSql::Tables).isEmpty()) {
        // Database is empty, populate it

        QFile dbSetup("database.sql");
        dbSetup.open(QIODevice::ReadOnly);
        QString instructions = dbSetup.readAll();
        QSqlQuery query;
        foreach (QString queryString, instructions.split("\n\n")){
            query.exec(queryString);
        }
    }
    return true;
}

QStringList Collection::listTags(QString filter)
{

    if (!ensureConnection()) return QStringList();

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true); // Save some memory,
                                // only allows seeking forward in the results

    QString q = "SELECT name FROM tag";
    if (!filter.isEmpty()) {
        query.prepare(q + "WHERE name LIKE '%?%'");
        query.addBindValue(filter);
    } else query.exec(q);

    if (!query.exec()) {
        qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
        return list;
    }

    // Loop through the results and append to our list
    while (query.next()) {
        list.append(query.value(0).toString());
    }

    return list;
}

/// Lists all videos given a list of tags
QList<Video> Collection::getVideos(QStringList tags)
{
    if (!ensureConnection()) return QList<Video>();

    QMutexLocker locker(&m_mutex);

    QList<Video> list;
    QSqlQuery query;

    query.setForwardOnly(true); // Save some memory,
                                // only allows seeking forward in the results

    QString q("SELECT video.name FROM video, tag, videoTag"
              " WHERE tag.id = videoTag.tagId"
              " AND video.id = videoTag.videoId");

    for (int i=0; i<tags.size(); i++)
        q.append(" AND tag.name LIKE ?");

    query.prepare(q);

    foreach (QString tag, tags)
        query.addBindValue(tag);


    if (!query.exec()) {
        qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
        return list;
    }

    locker.unlock();
    // Loop through the results and append to our list
    while (query.next()) {
        list.append(Video(query.value(0).toString()));
    }

    return list;
}

/// Gets either a list of tags in a named video
QStringList Collection::getTags(QString video)
{
    if (!ensureConnection()) return QStringList();

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true); // Save some memory,
                                // only allows seeking forward in the results

    query.prepare("SELECT tag.name FROM tag, video, videoTag"
                   " WHERE video.name LIKE ?"
                   " AND video.id = videoTag.videoId"
                   " AND tag.id = videoTag.tagId");

    query.addBindValue(video);

    if (!query.exec()) {
        qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
        return list;
    }

    // Loop through the results and append to our list
    while (query.next()) {
        list.append(query.value(0).toString());
    }

    return list;
}

QUrl Collection::getCover(QString video)
{
    if (!ensureConnection()) return QUrl();

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true);

    query.prepare("SELECT coverUrl FROM video WHERE video.name = ?");
    query.addBindValue(video);

    if (!query.exec()) {
        qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
        return QUrl("");
    }

    // Fetch the first result and return
    query.next();
    return QUrl(query.value(0).toString());
}

bool Collection::addTag(QString name, QString video)
{
    if (!ensureConnection()) return false;

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true);

    query.prepare("SELECT id FROM tag WHERE name = ?");

    query.addBindValue(name);


    if (!query.exec()) {
        qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
        return false;
    }

    if (query.size() == 0) { // Tag/Actress doesn't exist yet
        query.prepare("INSERT INTO tag(name) VALUES(?)");

        query.addBindValue(name);

        if (!query.exec()) {
            qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
            return false;
        }
    }

    query.prepare("INSERT INTO videoTag(videoId, tagId)"
                  "VALUES((SELECT DISTINCT id FROM video WHERE name=?), (SELECT DISTINCT id FROM tag WHERE name=?))");

    query.addBindValue(video);
    query.addBindValue(name);

    if (!query.exec()) {
        qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
        return false;
    }

    return true;
}


/// Gets a list of files for a video
QList<QUrl> Collection::getFiles(QString video)
{
    if (!ensureConnection()) return QList<QUrl>();

    QMutexLocker locker(&m_mutex);

    QList<QUrl> list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true); // Save some memory,
                                // only allows seeking forward in the results

    query.prepare("SELECT file.url FROM file, video"
                  " WHERE video.name = ?"
                  " AND video.id = file.videoId");

    query.addBindValue(video);

    if (!query.exec()) {
        qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
        return list;
    }

    // Loop through the results and append to our list
    while (query.next()) {
        list.append(query.value(0).toString());
    }

    return list;
}


QUrl Collection::getThumbnail(QString url)
{
    if (!ensureConnection()) return QUrl();

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true);

    query.prepare("SELECT thumbnail FROM file WHERE file.url = ?");
    query.addBindValue(url);

    query.exec();

    // Fetch the first result and return
    query.next();
    return QUrl(query.value(0).toString());
}

bool Collection::fileIsRegistered(QString url)
{
    if (!ensureConnection()) return false;

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true);

    query.prepare("SELECT url FROM file WHERE url = ?");
    query.addBindValue(url);

    if (!query.exec()) {
        qWarning() << Q_FUNC_INFO << ": Unable to execute query: " << query.lastError().text();
        return false;
    }

    // If the file exists already, we have a result
    return query.next();
}

void Collection::addFile(QString file, QString video)
{
    if (!ensureConnection()) return;

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true);

    query.prepare("INSERT INTO file(url, videoId) VALUES(?, (SELECT video.id FROM video WHERE video.name = ?))");
    query.addBindValue(file);
    query.addBindValue(video);
    query.exec();
}

void Collection::setThumbnail(QString file, QString thumbnail)
{
    if (!ensureConnection()) return;

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true);

    query.prepare("UPDATE file SET file.thumbnail = ? WHERE file.url = ?");
    query.addBindValue(thumbnail);
    query.addBindValue(file);

    query.exec();
}

void Collection::addVideo(QString name, QString cover, QString path, QStringList tags, QStringList files, QStringList thumbnails)
{
    if (!ensureConnection()) return;

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true);

    query.prepare("SELECT * FROM video WHERE name = ? OR path = ?");
    query.addBindValue(name);
    query.addBindValue(path);
    query.exec();

    if (query.next()) // either path or the video name exists already
        return; // TODO: some warning or sometihng?

    // Insert video into database
    query.prepare("INSERT INTO video(name, coverUrl, path) VALUES(?, ?, ?)");
    query.addBindValue(name);
    query.addBindValue(cover);
    query.addBindValue(path);
    query.exec();

    // Add tags to the video
    foreach (QString tag, tags)
        Collection::addTag(tag, name);

    // Add files to the video
    foreach (QString file, files)
        Collection::addFile(name, file);

    // Set the correct thumbnails for the files
    for (int i=0; i<files.size(); i++)
        Collection::setThumbnail(files[i], thumbnails[i]);
}

QString Collection::getPath(QString video)
{
    if (!ensureConnection()) return QString();

    QMutexLocker locker(&m_mutex);

    QStringList list;
    QSqlQuery query(m_db);
    query.setForwardOnly(true);

    query.prepare("SELECT path FROM video WHERE video.name = ?");
    query.addBindValue(video);

    query.exec();

    // Fetch the first result and return
    query.next();
    return query.value(0).toString();
}
